const filesize = require('filesize')
const markdownTable = require('markdown-table')
const loadConfig = require('./config').load

const buildTimeLimit = 2 * 60 * 1000
const buildMemoryLimit = 1 * 1024 * 1024 * 1024
const buildPerformanceSuggest = `建议尝试以下手段来优化构建性能。

- 拆分子项目：将此项目拆分成多个子项目，以分散构建压力。
- 提炼组件库：根据功能、场景、复杂度和稳定性等考量因素，将项目中的组件移动到独立的组件库项目中进行维护。
- 更新构建工具链和相关配置：新版的构建工具可能会有更好的性能。
`

const entrypointSizeLimit = 244 * 1024
const entrypointSizeSuggest = `
建议使用 [import()](https://webpack.js.org/guides/code-splitting/#dynamic-imports)、\
[SplitChunksPlugin()](https://webpack.js.org/guides/code-splitting/#splitchunksplugin)、\
[babel-plugin-import](https://www.npmjs.com/package/babel-plugin-import)、\
[异步组件](https://cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6)\
等手段来优化资源大小。
`

const getStringLength = (str) => str.length + [...str]
  .filter((ch) => ch.codePointAt(0) > 128).length

function boldColumn(results, rows, col, getter) {
  let max = -1
  let index = -1

  results.forEach((result, i) => {
    const val = getter(result, i)
    if (max < 0) {
      max = val
      return
    }
    if (val > max) {
      max = val
      index = i
    }
  })
  if (index >= 0) {
    // eslint-disable-next-line no-param-reassign
    rows[index][col] = `**${rows[index][col]}**`
  }
}

function sumSize(list, key = 'size') {
  return list.reduce((sum, item) => sum + item[key], 0)
}

function collectEntryAssets(report, entrypoints = Object.keys(report.entrypoints)) {
  const assetsMap = {}
  const assetsByEntry = {}

  report.assets.forEach((asset) => {
    assetsMap[asset.name] = asset
  })
  entrypoints.forEach((key) => {
    const entry = report.entrypoints[key]
    assetsByEntry[key] = entry.assets.map((name) => assetsMap[name])
      .filter((asset) => asset && !asset.name.endsWith('.map'))
  })
  return assetsByEntry
}

function writeEntrypointSizeSuggest(report) {
  const entrypoints = []
  const assetsByEntry = collectEntryAssets(report)

  Object.keys(assetsByEntry).forEach((name) => {
    const size = sumSize(assetsByEntry[name])

    if (size > entrypointSizeLimit) {
      entrypoints.push({ name, size })
    }
  })
  if (entrypoints.length > 0) {
    return [
      '\n\n**入口点资源大小限制：**\n\n',
      '以下入口点的合计资源大小已经超出建议的大小限制 ',
      `(${filesize(entrypointSizeLimit)})，这可能会影响网络性能。\n\n`,
      ...entrypoints.map((entry) => [
        `- ${entry.name} (${filesize(entry.size)})\n`,
        ...assetsByEntry[entry.name].map((asset) => `    - ${asset.name} (${filesize(asset.size)})\n`)
      ].join('')),
      entrypointSizeSuggest
    ].join('')
  }
  return ''
}

function writeBuildPerformanceSuggest(report) {
  let line = ''

  if (report.time > buildTimeLimit) {
    line = `当前构建耗时已经超出建议的时间限制 (${Math.floor(buildTimeLimit / 1000)} s)，`
  } else if (report.memory > buildMemoryLimit) {
    line = `当前构建进程占用的内存大小已经超出建议的大小限制 (${filesize(buildMemoryLimit)})，`
  } else {
    return ''
  }
  return [
    '\n\n**构建性能优化：**\n\n',
    line,
    buildPerformanceSuggest
  ].join('')
}

class GiteeWriter {
  constructor(config, repository) {
    this.config = config
    this.repository = repository
  }

  writeBuildReport(report) {
    const entryAssetsGroup = []
    const headerRow = ['版本', '构建耗时', '内存占用', '模块', '输出资源']
    const rows = report.results.map((result) => {
      const row = [
        `[${result.branch}](${this.repository.url}}/tree/${result.branch})`,
        `${result.time / 1000}s`,
        filesize(result.memory),
        `${result.modules.length} 个模块，共 ${filesize(sumSize(result.modules))}`,
        `${result.assets.length} 个文件，共 ${filesize(sumSize(result.assets))}`,
      ]

      if (this.config.entrypoints instanceof Array) {
        const assetsByEntry = collectEntryAssets(result, this.config.entrypoints.map(e => e.name))
        this.config.entrypoints.forEach((entry) => {
          const assets = assetsByEntry[entry.name]
          row.push(`${assets.length} 个文件，共 ${filesize(sumSize(assets))}`)
        })
        entryAssetsGroup.push(assetsByEntry)
      }
      return row
    })
    let suggests = [
      writeEntrypointSizeSuggest(report.results[0]),
      writeBuildPerformanceSuggest(report.results[0])
    ].join('')

    if (suggests) {
      suggests = [
        '\n\n<details>\n',
        '<summary>性能优化建议</summary>',
        suggests,
        '\n</details>'
      ].join('')
    }
    boldColumn(report.results, rows, 1, (result) => result.time)
    boldColumn(report.results, rows, 2, (result) => result.memory)
    boldColumn(report.results, rows, 3, (result) => sumSize(result.modules))
    boldColumn(report.results, rows, 4, (result) => sumSize(result.assets))
    if (this.config.entrypoints instanceof Array) {
      this.config.entrypoints.forEach((entry, i) => {
        headerRow.push(entry.title)
        boldColumn(report.results, rows, i + 5, (_, j) => sumSize(entryAssetsGroup[j][entry.name]))
      })
    }
    const lines = [
      '### 构建性能报告\n\n',
      markdownTable([headerRow, ...rows], { stringLength: getStringLength }),
      suggests
    ]
    report.results.forEach((result) => {
      if (result.error) {
        lines.push(
          `\n\n${result.branch} 分支构建失败！错误信息：\n\n`,
          '```text\n',
          result.message,
          '\n```\n'
        )
      }
    })
    return lines.join('')
  }

  writeLighthouseReport(report) {
    const createRow = (key) => ([
      report.audits[key].title,
      report.audits[key].displayValue,
      report.audits[key].description
    ])
    let auditKeys = [
      'first-contentful-paint',
      'largest-contentful-paint',
      'first-meaningful-paint',
      'speed-index',
      'interactive',
      'total-blocking-time',
      'cumulative-layout-shift',
      'mainthread-work-breakdown'
    ]
    if (this.config.audits instanceof Array) {
      const keys = this.config.audits.filter((key) => !!report.audits[key])
      if (keys.length > 0) {
        auditKeys = keys
      }
    }
    return [
      '### 页面性能测试报告\n\n',
      `使用 Lighthouse (${report.lighthouseVersion}) 对页面 ${report.finalUrl} 进行性能测试，`,
      `得到的性能评分为 **${report.categories.performance.score * 100}**，详细测试结果如下：\n`,
      markdownTable(
        [
          ['审核', '结果', '说明'],
          ...auditKeys.map(createRow)
        ],
        { stringLength: getStringLength }
      ),
      '\n\n**截图：**\n\n',
      `![截图](${report.audits['final-screenshot'].details.data})`
    ].join('')
  }

  write(report) {
    let markdown = ''

    if (report.build) {
      markdown += `${this.writeBuildReport(report.build)}\n\n`
    }
    if (report.lighthouse) {
      markdown += `${this.writeLighthouseReport(report.lighthouse)}\n\n`
    }
    if (!markdown) {
      return '未生成报告。'
    }
    return markdown
  }
}

function write(report, repository) {
  const config = loadConfig()

  if (config.upload.writer.name === 'gitee') {
    return new GiteeWriter(config.upload.writer, repository).write(report)
  }
  return ''
}

module.exports = {
  write
}
